package com.gitee.mocoffee.flow.spring;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.gitee.mocoffee.flow.Flow;
import com.gitee.mocoffee.flow.ProcessExceptionHandler;
import com.gitee.mocoffee.flow.Processor;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.experimental.FieldDefaults;

/**
 * @author lijia
 */
@FieldDefaults(level = AccessLevel.PRIVATE)
@NoArgsConstructor
@RequiredArgsConstructor
public class FlowFactory<INPUT>
		implements FactoryBean<Flow<INPUT>>, ApplicationContextAware, InitializingBean, DisposableBean {
	@Setter
	ApplicationContext applicationContext;

	@Setter
	@NonNull
	FlowConfigBean flowConfigBean;

	Flow<INPUT> instance;

	@Override
	public Flow<INPUT> getObject() throws Exception {
		return instance;
	}

	@Override
	public Class<?> getObjectType() {
		return Flow.class;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public void afterPropertiesSet() throws Exception {
		List<String[]> processorNames = flowConfigBean.getProcessorNames();
		Assert.notEmpty(processorNames, "处理器名称定义[processorNames]不能为空");

		List<Processor<INPUT>[]> chain = new ArrayList<>(processorNames.size());

		processorNames.forEach(pNames -> {
			Assert.noNullElements(pNames, "处理器名称定义[processorNames]不能含有空名称");
			Processor<INPUT>[] ps = new Processor[pNames.length];
			chain.add(ps);
			for (int i = 0; i < pNames.length; i++) {
				Processor p = applicationContext.getBean(pNames[i], Processor.class);
				ps[i] = p;
			}
		});

		instance = new Flow<INPUT>(chain);

		if (!StringUtils.isEmpty(flowConfigBean.getThreadPoolName()))
			instance.setThreadPool(
					applicationContext.getBean(flowConfigBean.getThreadPoolName(), ExecutorService.class));

		if (flowConfigBean.getThreadNum() != null)
			instance.setThreadNum(flowConfigBean.getThreadNum());

		if (!StringUtils.isEmpty(flowConfigBean.getExceptionHandlerName()))
			instance.setExceptionHandler(applicationContext.getBean(flowConfigBean.getExceptionHandlerName(),
					ProcessExceptionHandler.class));

		instance.init();
	}

	@Override
	public void destroy() throws Exception {
		instance.destroy();
	}
}
